All files / middleware auth.js

0% Statements 0/44
0% Branches 0/24
0% Functions 0/7
0% Lines 0/44

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147                                                                                                                                                                                                                                                                                                     
/**
 * Authentication Middleware
 * JWT token validation and role-based access control
 * 
 * @module middleware/auth
 */
 
const jwt = require('jsonwebtoken');
const { logger } = require('../config/logger');
 
/**
 * Verify JWT token and attach user to request
 */
function requireAuth(req, res, next) {
  const authHeader = req.headers.authorization;
 
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    logger.warn('Auth header missing or invalid', { path: req.path });
    return res.status(401).json({
      success: false,
      error: 'Access denied. Token not provided.',
    });
  }
 
  const token = authHeader.split(' ')[1];
 
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    req.userId = decoded.id;
    req.userRole = decoded.role;
    
    // Set tenant context if present in token
    if (decoded.tenantId) {
      req.tenantId = decoded.tenantId;
    }
    
    logger.debug('Token validated successfully', { userId: decoded.id, tenantId: decoded.tenantId });
    next();
  } catch (error) {
    logger.error('Token validation failed', { 
      error: error.message, 
      path: req.path
    });
    return res.status(401).json({
      success: false,
      error: 'Invalid or expired token.',
    });
  }
}
 
/**
 * Require Super Admin role
 */
function requireSuperAdmin(req, res, next) {
  if (!req.user || req.user.role !== 'superadmin') {
    return res.status(403).json({
      success: false,
      error: 'Access denied. Super Admin privileges required.',
    });
  }
  next();
}
 
/**
 * Require Admin role (tenant admin)
 */
function requireAdmin(req, res, next) {
  if (!req.user || (req.user.role !== 'admin' && req.user.role !== 'superadmin')) {
    return res.status(403).json({
      success: false,
      error: 'Access denied. Admin privileges required.',
    });
  }
  next();
}
 
/**
 * Require User role (any authenticated user)
 */
function requireUser(req, res, next) {
  if (!req.user) {
    return res.status(401).json({
      success: false,
      error: 'Authentication required.',
    });
  }
  next();
}
 
/**
 * Optional authentication
 * Attaches user if token is valid, but doesn't require it
 */
function optionalAuth(req, res, next) {
  const authHeader = req.headers.authorization;
 
  if (authHeader && authHeader.startsWith('Bearer ')) {
    const token = authHeader.split(' ')[1];
    
    try {
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
      req.user = decoded;
      req.userId = decoded.id;
      req.userRole = decoded.role;
      
      if (decoded.tenantId) {
        req.tenantId = decoded.tenantId;
      }
    } catch (error) {
      // Token invalid, but we don't fail the request
      logger.debug('Optional auth: Invalid token');
    }
  }
  
  next();
}
 
/**
 * Generate JWT token
 */
function generateToken(payload, expiresIn = '24h') {
  return jwt.sign(payload, process.env.JWT_SECRET, { expiresIn });
}
 
/**
 * Verify token without middleware
 */
function verifyToken(token) {
  try {
    return jwt.verify(token, process.env.JWT_SECRET);
  } catch (error) {
    return null;
  }
}
 
module.exports = {
  requireAuth,
  authenticateToken: requireAuth, // Alias for backward compatibility
  requireSuperAdmin,
  requireAdmin,
  requireUser,
  optionalAuth,
  generateToken,
  verifyToken,
};